﻿namespace MefContrib.Models.Provider
{
    using System;
    using System.Diagnostics;
    using System.Reflection;

    /// <summary>
    /// Provides an abstraction for working with a property on a reflection level.
    /// </summary>
    public class ComposableProperty : ComposableMember
    {
        /// <summary>
        /// Initializes a new instance of the <see cref="ComposableProperty"/> class
        /// for the specified <see cref="MemberInfo"/>.
        /// </summary>
        /// <param name="member">The member to make composable.</param>
        /// <exception cref="InvalidOperationException">The specified value for the member parameter was not a <see cref="PropertyInfo"/> instance.</exception>
        public ComposableProperty(MemberInfo member)
            : base(member)
        {
            PropertyInfo info = member as PropertyInfo;
            if (info == null)
            {
                throw new InvalidOperationException("The specified value for the member parameter was not a PropertyInfo instance.");
            }

            this.Property = info;

            MethodInfo getMethod = this.Property.GetGetMethod(false);
            if (getMethod != null)
            {
                this.ValueGetter =
                    getInstance =>
                    getMethod.Invoke(getInstance, null);
            }

            MethodInfo setMethod = this.Property.GetSetMethod(false);
            if (setMethod != null)
            {
                this.ValueSetter =
                    (setInstance, setValue) =>
                    setMethod.Invoke(setInstance, new[] { setValue });
            }
        }

        /// <summary>
        /// Gets a value indicating whether or not type of the <see cref="ComposableProperty"/> is of a lazy type or not.
        /// </summary>
        /// <value><see langword="true" /> if it is of a known lazy type; otherwise <see langword="false" />.</value>
        public override bool IsLazyType
        {
            get { return this.ReturnType.IsLazyType(); }
        }

        /// <summary>
        /// Gets a value indicating whether or not a value can be retrieved from the member.
        /// </summary>
        /// <value><see langword="true" /> if a value can be retrieved; otherwise <see langword="false" />.</value>
        public override bool IsReadable
        {
            [DebuggerStepThrough]
            get { return this.ValueGetter != null; }
        }

        /// <summary>
        /// Gets a value indicating whether or not an instance is needed for the value to be retrieved or assigned.
        /// </summary>
        /// <value><see langword="true" /> if an instance is needed; otherwise <see langword="false" />.</value>
        public override bool IsInstanceNeeded
        {
            [DebuggerStepThrough]
            get
            {
                MethodInfo info =
                    this.Property.GetGetMethod() ?? this.Property.GetSetMethod();
                return !info.IsStatic;
            }
        }

        /// <summary>
        /// Gets a value indicating whether or not a value can be assigned to the member.
        /// </summary>
        /// <value><see langword="true" /> if a value can be assigned; otherwise <see langword="false" />.</value>
        public override bool IsWritable
        {
            [DebuggerStepThrough]
            get { return this.ValueSetter != null; }
        }

        /// <summary>
        /// Gets the reference to the property.
        /// </summary>
        /// <value>A <see cref="PropertyInfo"/> object.</value>
        public PropertyInfo Property { get; protected set; }
        
        /// <summary>
        /// Gets the <see cref="Type"/> of the return value of the <see cref="ComposableProperty"/>.
        /// </summary>
        /// <value>A <see cref="Type"/> object.</value>
        public override Type ReturnType
        {
            [DebuggerStepThrough]
            get { return this.Property.PropertyType; }
        }
    }
}